using System.Collections.Generic;
using Microsoft.Xna.Framework;

using Programonica.Igramonica.Core;

namespace Programonica.Igramonica.OctTreeClasses
{
    public static class OctTreeNodeGenerator
    {
        public static OctTreeNode Generate(BoundingBox sceneSize, List<IComponentBase> components)
        {
            OctTreeNode rootNode = GenerateNodes(sceneSize);

            FillNodes(rootNode, components);

            return rootNode;
        }

        private static OctTreeNode GenerateNodes(BoundingBox sceneSize)
        {
            int rootNodeSize = OctTreeSettings.LeafSize;

            for (int i = 0; i < OctTreeSettings.LevelsCount - 1; i++)
            {
                rootNodeSize *= 4;
            }

            BoundingBox discreteSceneSize = MathUtils.GetDiscreteBox(sceneSize, rootNodeSize);

            GenerateSettings(discreteSceneSize);

            OctTreeNode rootNode = new OctTreeNode(discreteSceneSize, 0);
            
            //rootNode.GenerateChildNodes();

            return rootNode;
        }

        private static void GenerateSettings(BoundingBox rootBoundingBox)
        {
            for (int level = 0; level < OctTreeSettings.LevelsCount; level++)
            {
                int levelNodeSize = OctTreeSettings.LeafSize;

                for (int i = 0; i < (OctTreeSettings.LevelsCount - 1) - level; i++)
                {
                    levelNodeSize *= 4;
                }

                OctTreeSettings.LevelsNodesSize[level] = levelNodeSize;

                if (level < (OctTreeSettings.LevelsCount))
                {
                    if (level == 0)
                    {
                        OctTreeSettings.LevelsNodesCount[level] = (rootBoundingBox.Max - rootBoundingBox.Min) / levelNodeSize;
                    }
                    else
                    {
                        OctTreeSettings.LevelsNodesCount[level] = new Vector3(4);
                    }
                }
            }
        }

        private static void FillNodes(OctTreeNode rootNode, List<IComponentBase> components)
        {
            foreach (OctTreeComponent component in components)
            {
                if (!component.IsStatic)
                {
                    continue;
                }
                
                BoundingBox componentBox = component.GetBox();

                BoundingBox discreteComponentBox = MathUtils.GetDiscreteBox(componentBox, OctTreeSettings.LeafSize);

                for (float x = discreteComponentBox.Min.X; x < discreteComponentBox.Max.X; x += OctTreeSettings.LeafSize)
                {
                    for (float y = discreteComponentBox.Min.Y; y < discreteComponentBox.Max.Y; y += OctTreeSettings.LeafSize)
                    {
                        for (float z = discreteComponentBox.Min.Z; z < discreteComponentBox.Max.Z; z += OctTreeSettings.LeafSize)
                        {
                            Vector3 leafBoxCorner = new Vector3(x, y, z);

                            BoundingBox leafBox = new BoundingBox(leafBoxCorner, leafBoxCorner + new Vector3(OctTreeSettings.LeafSize));

                            ContainmentType containmentType = component.Contains(leafBox);
                            
                            if (containmentType == ContainmentType.Contains ||
                                containmentType == ContainmentType.Intersects)
                            {
                                OctTreeLeaf leaf = (OctTreeLeaf)rootNode.FindLeaf(leafBoxCorner);

                                if (leaf.Components == null)
                                {
                                    leaf.Components = new List<OctTreeComponent>();
                                }

                                leaf.Components.Add(component);
                            }
                        }
                    }
                }
            }
        }
    }
}